/*******************************************************************************
*  file    : windmainwindow.cpp
*  created : 10.07.2014
*  author  : Slyshyk Oleksiy (alexSlyshyk@gmail.com)
*******************************************************************************/

#include "windmainwindow.hpp"
#include <QDir>
#include <QTimer>
#include <QAction>
#include <QToolBar>
#include <QSettings>
#include <QFileDialog>
#include <QDockWidget>
#include <QApplication>
#include <QTextBrowser>
#include <QPluginLoader>

#include <qlogger.hpp>
#include <interfaces.hpp>
#include <transformwindow.hpp>

#include "windwidget.hpp"
#include "calcplotwidget.hpp"
#include "filedataprovider.hpp"
#include "networkdataprovider.hpp"

namespace
{
QLogger * l = QLoggerEngine::getLogger("Core");
QLogger * s = QLoggerEngine::getLogger("Solution");
}

windMainWindow::windMainWindow(QWidget *parent) :
    QMainWindow(parent)
{
    tabWidget_ = new QTabWidget(this);
    tabWidget_->setTabPosition(QTabWidget::West);
    windWidget_ = new WindWidget(this);
    tabWidget_->addTab(windWidget_, QStringLiteral("ветер"));
    calcPlotWidget_ = new CalcPlotWidget(this);
    tabWidget_->addTab(calcPlotWidget_, QStringLiteral("график"));
    setCentralWidget(tabWidget_);
    logBrowser_ = new QTextBrowser();
    addDock(tr("Log messages"), logBrowser_, Qt::BottomDockWidgetArea);

    transformRespWnd_ = new TransformWindow;
    addDock(tr("Расчеты Ответа"), transformRespWnd_, Qt::LeftDockWidgetArea);
    transformPulseWnd_ = new TransformWindow;
    addDock(tr("Расчеты старта"), transformPulseWnd_, Qt::LeftDockWidgetArea);
    transformPulseWnd_->setSettingsGroup("transform_pulse");

    QLoggerObjectAppender * objAppender = new QLoggerObjectAppender(127, "%d - <%l> - {%c} - %m");
    l->addAppender(objAppender);
    s->addAppender(objAppender);
    connect(objAppender, SIGNAL(logStr(QString)), this, SLOT(addLogMessage(QString)) );

    resetSolutions();

    loadPlugins   ();
    ctrateToolBars();
    loadWndState  ();

    solutions_->setTransformPlugins(transformPlugins_);
    solutions_->setTransformIntPlugins(transformIntPlugins_);

    //setFileDataProvider();
    QTimer::singleShot(50 , this, SLOT(fillTransformWnd()));
}

void windMainWindow::resetSolutions()
{
    solutions_.reset(new Solutions);
    connect(windWidget_, &WindWidget::responceTimeChanged,
            [this](){solutions_->setResponceTime(windWidget_->responceTimeL(),windWidget_->responceTimeR());} );
    windWidget_->setSolutions(solutions_.data());
    if(dataProvider_)
        {
            connect(dataProvider_.data(), SIGNAL(dataReady  (int,NamedDataPtr)) ,
                    solutions_.data()   , SLOT  (onDataReady(int,NamedDataPtr)) ,
                    static_cast<Qt::ConnectionType>(Qt::AutoConnection | Qt::UniqueConnection) );
            connect(dataProvider_.data(), SIGNAL(dataObjectReady  (DataObjectPtr)),
                    solutions_.data()   , SLOT  (onDataObjectReady(DataObjectPtr)),
                    static_cast<Qt::ConnectionType>(Qt::AutoConnection | Qt::UniqueConnection));
        }

    solutions_->setTransformPlugins(transformPlugins_);
    solutions_->setTransformIntPlugins(transformIntPlugins_);
    solutions_->setParams          (transformPulseWnd_->params(), transformRespWnd_->params());
    solutions_->setResponceTime    (windWidget_->responceTimeL(),windWidget_->responceTimeR());
    solutions_->setTemperature     (windWidget_->temperature ());
}

void windMainWindow::closeEvent(QCloseEvent *event)
{
    saveWndState();
    QMainWindow::closeEvent(event);
}

void windMainWindow::saveWndState()
{
    QSettings settings;
    settings.beginGroup("main_window");
    settings.setValue("geometry", saveGeometry());
    settings.setValue("windowState", saveState());
    settings.endGroup();

    settings.beginGroup("file_data_provider");
    FileDataProvider* dp = dynamic_cast<FileDataProvider*>(dataProvider_.data());
    if(dp)
        settings.setValue("path",dp->path());
    settings.endGroup();

    settings.beginGroup("wind_window");
    settings.setValue("temperature",windWidget_->temperature());
    settings.setValue("responce_time_L",windWidget_->responceTimeL());
    settings.setValue("responce_time_R",windWidget_->responceTimeR());
    settings.endGroup();

    settings.beginGroup("data_provider");
    settings.setValue("name",dataProvider_->name());
    settings.endGroup();

    transformRespWnd_->saveConfig(&settings);
    transformPulseWnd_->saveConfig(&settings);
}

void windMainWindow::loadWndState()
{
    QSettings settings;
    settings.beginGroup("main_window");
    restoreGeometry(settings.value("geometry").toByteArray());
    restoreState   (settings.value("windowState").toByteArray());
    settings.endGroup();

    settings.beginGroup("wind_window");
    double t_l,t_r;
    t_l = settings.value("responce_time_L",0.0001).toDouble();
    t_r = settings.value("responce_time_R",0.0001).toDouble();
    windWidget_->setResponceTime(t_l,t_r);
    windWidget_->setTemperature(settings.value("temperature",20.0).toDouble());
    settings.endGroup();

    settings.beginGroup("data_provider");
    QString data_provider = settings.value("name","FileDataProvider").toString();
    settings.endGroup();

    if(data_provider == "FileDataProvider")
        QTimer::singleShot(350, this, SLOT(setFileDataProvider()));
    else if(data_provider == "NetworkDataProvider")
        QTimer::singleShot(350, this, SLOT(setNetworkDataProvider()));
    else
        logError(l) << "Not found data provider " << data_provider;
}

void windMainWindow::changeEvent(QEvent *e)
{
    QWidget::changeEvent(e);
    switch (e->type())
        {
            case QEvent::LanguageChange:

                break;
            default:
                break;
        }
}

void windMainWindow::ctrateToolBars()
{
    mainToolBar_ = new QToolBar(this);
    mainToolBar_->setObjectName("main_toolbar");
    windToolBar_ = new QToolBar(this);
    windToolBar_->setObjectName("wind_toolbar");

    QIcon icon;
    calculateAct_ = new QAction(tr("Расчеты"),this);
    calculateAct_->setCheckable(true);
    calculateAct_->setShortcut(QKeySequence("Ctrl+T"));
    calculateAct_->setShortcutContext(Qt::ApplicationShortcut);
    icon.addPixmap(QPixmap(tr("://images/funct.png")), QIcon::Normal, QIcon::Off);
    calculateAct_->setIcon(icon);

    recalcDirAct_ = new QAction(tr("Пересчитать папку"),this);
    recalcDirAct_->setShortcut(QKeySequence("Ctrl+T"));
    recalcDirAct_->setShortcutContext(Qt::ApplicationShortcut);
    icon.addPixmap(QPixmap(tr("://images/reload.png")), QIcon::Normal, QIcon::Off);
    recalcDirAct_->setIcon(icon);
    connect(recalcDirAct_, &QAction::triggered, [this](bool){
            FileDataProvider* fdp = dynamic_cast<FileDataProvider*>(dataProvider_.data());
            solutions_->setParams(transformPulseWnd_->params(), transformRespWnd_->params());
            if(fdp)
                fdp->processExistFiles(windWidget_->path());
        });

    solutionFlushAct_ = new QAction(tr("Сбросить графики"), this);
    solutionFlushAct_->setShortcut(QKeySequence("Ctrl+F"));
    solutionFlushAct_->setShortcutContext(Qt::ApplicationShortcut);
    icon.addPixmap(QPixmap(tr("://images/editclear.png")), QIcon::Normal, QIcon::Off);
    solutionFlushAct_->setIcon(icon);
    connect(solutionFlushAct_, &QAction::triggered, [this](bool){this->resetSolutions();}  );

    setFolderDataAct_ = new QAction(tr(""), this);
    icon.addPixmap(QPixmap(tr("://images/folder.png")), QIcon::Normal, QIcon::Off);
    setFolderDataAct_->setIcon(icon);
    connect(setFolderDataAct_, &QAction::triggered, [this](bool){setFileDataProvider();} );

    setNetworkDataAct_ = new QAction(tr(""), this);
    icon.addPixmap(QPixmap(tr("://images/socket.png")), QIcon::Normal, QIcon::Off);
    setNetworkDataAct_->setIcon(icon);
    connect(setNetworkDataAct_, &QAction::triggered, [this](bool){setNetworkDataProvider();} );

    clearPlotAct_   = new QAction(tr(""),this);
    icon.addPixmap(QPixmap(tr("://images/draw_eraser.png")), QIcon::Normal, QIcon::Off);
    clearPlotAct_->setIcon(icon);
    connect(clearPlotAct_, &QAction::triggered, [this](bool){calcPlotWidget_->clearPlots();} );
    findPatternsAct_= new QAction(tr(""),this);
    icon.addPixmap(QPixmap(tr("://images/draw_vertex.png")), QIcon::Normal, QIcon::Off);
    findPatternsAct_->setIcon(icon);
    connect(findPatternsAct_, &QAction::triggered, [this](bool){this->findPatterns();} );
    findBaseTimeAct_ = new QAction(tr(""),this);
    icon.addPixmap(QPixmap(tr("://images/balance.png")), QIcon::Normal, QIcon::Off);
    findBaseTimeAct_->setIcon(icon);
    connect(findBaseTimeAct_, &QAction::triggered, [this](bool){this->findBaseTime();});

    mainToolBar_->addAction(calculateAct_);
    mainToolBar_->addAction(recalcDirAct_);
    mainToolBar_->addAction(solutionFlushAct_);
    mainToolBar_->addAction(setFolderDataAct_);
    mainToolBar_->addAction(setNetworkDataAct_);

    windToolBar_->addAction(findPatternsAct_);
    windToolBar_->addAction(findBaseTimeAct_);
    windToolBar_->addAction(clearPlotAct_);
    addToolBar(Qt::TopToolBarArea, mainToolBar_);
    addToolBar(Qt::TopToolBarArea, windToolBar_);
}

void windMainWindow::addDock(QString title, QWidget *w, Qt::DockWidgetArea area)
{
    QDockWidget * dock = new QDockWidget(title, this);
    dock->setObjectName(title.replace(QString(" "),QString("_"),Qt::CaseInsensitive));
    dock->setWidget(w);
    this->addDockWidget(area, dock);
}

void windMainWindow::loadPlugins()
{
    transformPlugins_.reset(new QMap<QString, TransformNamedData*>);
    transformIntPlugins_.reset(new QMap<QString, TransformNamedIntData*>);
    formatPlugins_.reset(new QMap<QString, ReadFileFormatInterface*>);
    saveLoadObjectPlugins_.reset(new QMap<QString, SaveLoadObjectInterface*>);
    QDir pluginsDir = QDir(qApp->applicationDirPath());
    pluginsDir.cd("plugins");
    foreach (QString fileName, pluginsDir.entryList(QDir::Files))
        {
            QPluginLoader loader(pluginsDir.absoluteFilePath(fileName));
            QObject *plugin = loader.instance();
            if (plugin)
                {
                    populatePlugins(plugin);
                }
        }
}

void windMainWindow::populatePlugins(QObject *plugin)
{
    if(plugin)
        {
            TransformNamedData* iTransform = qobject_cast<TransformNamedData*>(plugin);
            if(iTransform)
                {
                    (*transformPlugins_)[iTransform->name()] = iTransform;
                    logInfo(l) << QStringLiteral("Загружен плагин ") << iTransform->name();
                }
            TransformNamedIntData* iTransformInt = qobject_cast<TransformNamedIntData*>(plugin);
            if(iTransformInt)
                {
                    (*transformIntPlugins_)[iTransformInt->name()] = iTransformInt;
                    logInfo(l) << QStringLiteral("Загружен плагин ") << iTransformInt->name();
                }
            ReadFileFormatInterface * iFormat = qobject_cast<ReadFileFormatInterface *>(plugin);
            if(iFormat)
                {
                    (*formatPlugins_)[iFormat->name()] = iFormat;
                    logInfo(l) << QStringLiteral("Загружен плагин ") << iFormat->name();
                }
            SaveLoadObjectInterface* iSaveLoadObject = qobject_cast<SaveLoadObjectInterface*>(plugin);
            if(iSaveLoadObject)
                {
                    (*saveLoadObjectPlugins_)[iSaveLoadObject->name()] = iSaveLoadObject;
                    logInfo(l) << QStringLiteral("Загружен плагин ") << iSaveLoadObject->name();
                }
        }
}

void windMainWindow::setFileDataProvider()
{
    QSettings settings;
    FileDataProvider* dp = new FileDataProvider;
    dp->setObjectPlugins(saveLoadObjectPlugins_);
    dp->setFormatPlugins(formatPlugins_);
    settings.beginGroup("file_data_provider");
    windWidget_->setPath( settings.value("path").toString() );
    dp->setWatcherPath(windWidget_->path());
    settings.endGroup();
    dataProvider_.reset(dp);
    resetSolutions();
    connect(windWidget_, &WindWidget::pathChanged, static_cast<FileDataProvider*>(dataProvider_.data()), &FileDataProvider::setWatcherPath );
    connect(windWidget_, &WindWidget::pathChanged, [this](const QString&){this->saveWndState();});
}

void windMainWindow::setNetworkDataProvider()
{
    NetworkDataProvider* ndp = new NetworkDataProvider;
    ndp->connectToHost(QHostAddress("127.0.0.1"));

    dataProvider_.reset(ndp);
    resetSolutions();
}

void windMainWindow::findPatterns()
{
    QString file_name = QFileDialog::getOpenFileName(this, tr("Открыть файл..."), last_opened_dir_/*, filters.join(";;")*/);
    if(file_name.size())
        {
            QFileInfo fi(file_name);
            last_opened_dir_ = fi.absoluteDir().absolutePath();
            FileDataProvider* provider ;
            if(dataProvider_->name() != QStringLiteral("FileDataProvider"))
                setFileDataProvider();

            provider = dynamic_cast<FileDataProvider*>(dataProvider_.data());
            if(provider)
                {
                    auto data_obj = provider->loadDataObject(file_name);
                    calcPlotWidget_->findPatterns(data_obj.data());
                }
        }
}

void windMainWindow::findBaseTime()
{
    setFileDataProvider();
    FileDataProvider* provider = dynamic_cast<FileDataProvider*>(dataProvider_.data());
    if(provider)
        {
            solutions_->setTaskSize(provider->filesQuantity(provider->path()));
            provider->processExistFiles(windWidget_->path());
            connect(solutions_.data(), SIGNAL(taskDone()),this, SLOT(solutuinsTaskDone()),
                    static_cast<Qt::ConnectionType>(Qt::AutoConnection | Qt::UniqueConnection) );
        }
}

void windMainWindow::solutuinsTaskDone()
{
    logInfo(l) << "Task done";
    calcPlotWidget_->findBaseTime(solutions_.data());
}

void windMainWindow::addLogMessage(QString txt)
{
    logBrowser_->append(txt);
}

void windMainWindow::fillTransformWnd()
{
    for(const auto& t : solutions_->transformationList())
        transformRespWnd_->addTransformation(t);
    transformRespWnd_->updatePatternList(solutions_->patternList());

    transformRespWnd_->loadConfig();

    for(const auto& t : solutions_->transformationList())
        transformPulseWnd_->addTransformation(t);
    transformPulseWnd_->updatePatternList(solutions_->patternList());

    transformPulseWnd_->loadConfig();

    solutions_->setParams      (transformPulseWnd_->params(), transformRespWnd_->params());
    solutions_->setResponceTime(windWidget_->responceTimeL(),windWidget_->responceTimeR());
    solutions_->setTemperature (windWidget_->temperature ());
}

